import java.util.Arrays;



public class SnowCleaningVisDirect {
	public static String MOVE_REG_EXP = "M [1-9]?[0-9] [ULDR]";
	public static String HIRE_REG_EXP = "H [1-9]?[0-9] [1-9]?[0-9]";

	public static String WRONG_COMMAND_ERROR = "ERROR: Each worker command must be formatted either \"M <ID> <DIR>\""
			+ " or \"H <ROW> <COL>\". Here <ID>, <ROW> and <COL> are integers from 0 to 99 without leading zeros"
			+ " and <DIR> is one of 'U', 'L', 'D', 'R'.";

	public static String execCommand = null;
	public static long seed = 1;
	public static boolean vis = true;
	public static int cellSize = 12;
	public static int delay = 100;
	public static boolean startPaused = false;

	// public static Process solution;

	public int runTest() {
		// solution = null;
		SnowCleaning sc = new SnowCleaning();

		TestCase tc = new TestCase(seed);

		sc.init(tc.boardSize, tc.salary, tc.snowFine);

		World world = new World(tc.boardSize, tc.salary, tc.snowFine);
		Drawer drawer = null;
		if (vis) {
			drawer = new Drawer(world, cellSize);
			if (startPaused) {
				drawer.pauseMode = true;
			}
		}

		for (int t = 0; t < Constants.SIMULATION_TIME; t++) {
			world.startNewDay();

			int snowFallCnt = tc.snowFalls[t].size();
			Cell[] snowFalls = new Cell[snowFallCnt];
			int pos = 0;
			for (Cell cell : tc.snowFalls[t]) {
				snowFalls[pos++] = cell;
			}
			Arrays.sort(snowFalls);

			// StringBuilder sb = new StringBuilder();
			// sb.append(snowFallCnt).append("\n");
			int[] snowFallsArray = new int[snowFallCnt * 2];
			for (int i = 0; i < snowFallCnt; i++) {
				// sb.append(snowFalls[i].r).append("\n").append(snowFalls[i].c).append("\n");
				snowFallsArray[i * 2] = snowFalls[i].r;
				snowFallsArray[i * 2 + 1] = snowFalls[i].c;
				world.addSnow(snowFalls[i].r, snowFalls[i].c);
			}


			// writer.print(sb.toString());
			// writer.flush();
			String[] commands = sc.nextDay(snowFallsArray);

			int commandCnt;
			try {
				// commandCnt = Integer.parseInt(reader.readLine());
				commandCnt = commands.length;
			} catch (Exception e) {
				System.err.println("ERROR: time step = " + t + " (0-based). Unable to get the number of worker commands"
						+ " from your solution.");
				return -1;
			}

			for (int i = 0; i < commandCnt; i++) {
				String command;
				try {
					// command = reader.readLine();
					command = commands[i];
				} catch (Exception e) {
					System.err.println("ERROR: time step = " + t + " + (0-based). Unable to read " + i + "-th (0-based)"
							+ " worker command from your solution.");
					return -1;
				}
				if (command.length() > 10) {
					System.err.println("ERROR: time step = " + t + ", worker command = " + i + " (0-based indices). "
							+ WRONG_COMMAND_ERROR);
					return -1;
				}
				if (command.matches(HIRE_REG_EXP)) {
					String[] items = command.split(" ");
					int row = Integer.parseInt(items[1]);
					int col = Integer.parseInt(items[2]);
					String msg = world.addWorker(row, col);
					if (msg.length() > 0) {
						System.err.println("ERROR: time step = " + t + ", worker command = " + i + " (0-based indices). " + msg);
						return -1;
					}
				} else if (command.matches(MOVE_REG_EXP)) {
					String[] items = command.split(" ");
					int id = Integer.parseInt(items[1]);
					int dir = Constants.DIR_STR.indexOf(items[2]);
					String msg = world.moveWorker(id, dir);
					if (msg.length() > 0) {
						System.err.println("ERROR: time step = " + t + ", worker command = " + i + " (0-based indices). " + msg);
						return -1;
					}
				} else {
					System.err.println("ERROR: time step = " + t + ", worker command = " + i + " (0-based indices). "
							+ WRONG_COMMAND_ERROR);
					return -1;
				}
			}

			world.cleanAllSnow();

			world.updateTotalFine();
			world.updateTotalSalary();

			if (vis) {
				drawer.processPause();
				drawer.repaint();
				try {
					Thread.sleep(delay);
				} catch (Exception e) {
					// do nothing
				}
			}
		}

		stopSolution();

		System.out.println("Fine   = " + world.totFine);
		System.out.println("Salary = " + world.totSalary);

		// System.out.println("Internal Fine = " + sc.world.totFine + " Salary= " + sc.world.totSalary);

		return world.totFine + world.totSalary;
	}

	public static void stopSolution() {
		// if (solution != null) {
		// try {
		// solution.destroy();
		// } catch (Exception e) {
		// // do nothing
		// }
		// }
	}

	public static int main(String[] args) {
		for (int i = 0; i < args.length; i++)
			if (args[i].equals("-exec")) {
				execCommand = args[++i];
			} else if (args[i].equals("-seed")) {
				seed = Long.parseLong(args[++i]);
			} else if (args[i].equals("-novis")) {
				vis = false;
			} else if (args[i].equals("-sz")) {
				cellSize = Integer.parseInt(args[++i]);
			} else if (args[i].equals("-delay")) {
				delay = Integer.parseInt(args[++i]);
			} else if (args[i].equals("-pause")) {
				startPaused = true;
			} else {
				System.out.println("WARNING: unknown argument " + args[i] + ".");
			}


		SnowCleaningVisDirect vis = new SnowCleaningVisDirect();
		try {
			int score = vis.runTest();
			System.out.println("Score  = " + score);
			return score;
		} catch (RuntimeException e) {
			System.err.println("ERROR: Unexpected error while running your test case.");
			e.printStackTrace();
			SnowCleaningVisDirect.stopSolution();
			throw e;
		}

	}
}
